﻿//-----------------------------------------------------------------------
// <copyright file="Rect2D.cs" company="Nathan Miller">
// Copyright (c) Nathan Miller.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
//-----------------------------------------------------------------------
namespace TileTool.Common.Math
{
    using System;

    /// <summary>
    /// 2D rectangle structure for a cartesian coordinate system.
    /// </summary>
    public struct Rect2D
    {
        #region Fields - Private

        /// <summary>
        /// Size of the rectangle.
        /// </summary>
        private Size2D size;

        /// <summary>
        /// Position of the rectangle.
        /// </summary>
        private Vector2D pos;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor which takes a position, width and height.
        /// </summary>
        /// <param name="pos">Rectnagle position.</param>
        /// <param name="w">Rectangle width.</param>
        /// <param name="h">Rectangle height.</param>
        public Rect2D(Vector2D pos, float w, float h)
        {
            if (w < 0.0f)
            {
                throw new ArgumentException("Width must be >= 0.0f");
            }

            if (h < 0.0f)
            {
                throw new ArgumentException("Height must be >= 0.0f");
            }

            this.pos = pos;
            this.size = new Size2D(w, h);
        }

        /// <summary>
        /// Constructor which takes a position, width and height.
        /// </summary>
        /// <param name="pos">Rectnagle position.</param>
        /// <param name="size">Size of the rect.</param>
        public Rect2D(Vector2D pos, Size2D size)
        {
            this.pos = pos;
            this.size = size;
        }

        #endregion

        #region Properties - Public

        /// <summary>
        /// Gets or sets the lower left position of the rectangle.
        /// </summary>
        public Vector2D Pos
        {
            get
            {
                return this.pos;
            }

            set
            {
                this.pos = value;
            }
        }

        /// <summary>
        /// Gets or sets the size of the rectangle.
        /// </summary>
        public Size2D Size
        {
            get
            {
                return this.size;
            }

            set
            {
                this.size = value;
            }
        }

        /// <summary>
        /// Gets the center of the rectangle.
        /// </summary>
        public Vector2D Center
        {
            get
            {
                return new Vector2D(this.pos.x + (this.size.Width * 0.5f), this.pos.y + (this.size.Height * 0.5f));
            }
        }

        /// <summary>
        /// Gets or sets the width of the rectangle.
        /// </summary>
        public float Width
        {
            get
            {
                return this.size.Width;
            }

            set
            {
                this.size.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the height of the rectangle.
        /// </summary>
        public float Height
        {
            get
            {
                return this.size.Height;
            }

            set
            {
                this.size.Height = value;
            }
        }

        /// <summary>
        /// Gets the left X position of the rectangle.
        /// </summary>
        public float Left
        {
            get
            {
                return this.pos.x;
            }
        }

        /// <summary>
        /// Gets the right X position of the rectangle.
        /// </summary>
        public float Right
        {
            get
            {
                return this.pos.x + this.Width;
            }
        }

        /// <summary>
        /// Gets the top Y position of the rectangle.
        /// </summary>
        public float Top
        {
            get
            {
                return this.pos.y + this.Height;
            }
        }

        /// <summary>
        /// Gets the bottom Y position of the rectangle.
        /// </summary>
        public float Bottom
        {
            get
            {
                return this.pos.y;
            }
        }

        /// <summary>
        /// Gets the south west point of the rect.
        /// </summary>
        public Vector2D SouthWest
        {
            get
            {
                return this.Pos;
            }
        }

        /// <summary>
        /// Gets the south east point of the rect.
        /// </summary>
        public Vector2D SouthEast
        {
            get
            {
                return new Vector2D(this.Pos.x + this.Width, this.Pos.y);
            }
        }

        /// <summary>
        /// Gets the north east point of the rect.
        /// </summary>
        public Vector2D NorthEast
        {
            get
            {
                return new Vector2D(this.Pos.x + this.Width, this.Pos.y + this.Height);
            }
        }

        /// <summary>
        /// Gets the north west point of the rect.
        /// </summary>
        public Vector2D NorthWest
        {
            get
            {
                return new Vector2D(this.Pos.x, this.Pos.y + this.Height);
            }
        }

        #endregion

        #region Methods - Public

        /// <summary>
        /// Tests to see if the passed point is inside the rectangle.
        /// </summary>
        /// <param name="point">Point to test.</param>
        /// <returns>Returns true if the point is inside the rectangle.</returns>
        public bool Contains(Vector2D point)
        {
            Vector2D localPos = point - this.Pos;

            if ((localPos.x < 0.0f) || (localPos.x > this.Width))
            {
                return false;
            }

            if ((localPos.y < 0.0f) || (localPos.y > this.Height))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Gets an array with the points that make up the rectangle.
        /// 
        /// The returned vector will always be of size 4 and the points will be ordered as follows:
        ///     Point 0 - Lower left - SW
        ///     Point 1 - Lower right - SE
        ///     Point 2 - Upper right - NE
        ///     Point 3 - Upper left - NW
        /// </summary>
        /// <returns>Returns an array with the points of the rectangle.</returns>
        public Vector2D[] GetPoints()
        {
            Vector2D[] points = new Vector2D[4];

            points[0] = this.SouthWest;
            points[1] = this.SouthEast;
            points[2] = this.NorthEast;
            points[3] = this.NorthWest;

            return points;
        }

        #endregion
    }
}
